package controllers;

import java.awt.event.ActionEvent;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

import javax.swing.JOptionPane;

import model.AddNewLayersModel;
import model.LayerModel;
import model.SearchPhotoModel;
import client.CliMessage;
import view.AddNewLayersGUI;
import client.ImageSerializer;

/**
 * The Class AddNewLayersController.
 * @author Tomer
 */
public class AddNewLayersController  extends AbstractController
{
	
	/** The add new layer view. */
	private AddNewLayersGUI addNewLayerView;
	
	/** The add new layers model. */
	private AddNewLayersModel addNewLayersModel;
	
	/** The layer lists. */
	ArrayList<ArrayList<LayerModel>> layerLists;

	private String[] layerNamesArray;

	private LayerModel tempLayer;

	private boolean hasLayer = false;

	/**
	 * Instantiates a new adds the new layers controller.
	 *
	 * @param addNewLayerView the add new layer view
	 * @param addNewLayersModel the add new layers model
	 */
	AddNewLayersController(AddNewLayersGUI addNewLayerView,AddNewLayersModel addNewLayersModel)
	{
		super(addNewLayerView, addNewLayersModel);
		this.addNewLayerView = addNewLayerView;
		this.addNewLayersModel = addNewLayersModel;
		
		//set the array of lists
		layerLists = new ArrayList<ArrayList<LayerModel>>();
	}

	/**
	 * get all layers of the photo from the DB and save them in a list.
	 *
	 * @param dbResult the db result
	 */
	
	@Override
	public void handleDBSearchResult(CliMessage dbResult) 
	{
		//the first time this method is called - 
		//	the serialized result set contains all the possible layer types
		//at the second time this method is called  -  
		// the serialized result set contains all the layers of the current presented photo
		
		ArrayList<LayerModel> layerList = new ArrayList<LayerModel>();
		
		srs = dbResult.getSrs();
		int nRows = srs.size();
		
		//get all tuples
		for(int i = 0 ; i < nRows ; i++)
		{
			// get a tuple
			HashMap<String, String> layerTuple = srs.get(i);
			
			//get the time stamp , location and the link to file
			int layerType = Integer.parseInt(layerTuple.get("LAYERTYPE"));
			String layerName = layerTuple.get("LAYERNAME");
			String linkToFile = null;
			//if this is the second time this method is called
			if(layerLists.size() > 0)
				linkToFile = layerTuple.get("LINKTOFILE");
			
			//set a photo detail class
			LayerModel layerDetails = new LayerModel(layerType,layerName,linkToFile) ;
			
			//add the photo details to the list
			layerList.add(layerDetails);
			
			//display for developer
			System.out.println( layerName+" "+layerType+" "+linkToFile);	
		}
		
		layerLists.add(layerList);	
		
		//if the second list that was added is the current photo layer list 
		// - call the 
		if(layerLists.size() == 2)
			addLayerToPhoto();
		if(layerLists.size() == 1)
			setLayerListInGUI();
	}
	
	/**
	 * the current photo has no layers - add the new layer
	 */
	@Override
	public void handleNullSearchResult() 
	{
		//get selected layer
		String selectedLayerName = (String) addNewLayerView.getSelectedItem();

		//serialize the layer
		String layerFilePath = addNewLayerView.getLinkTofileTextField();
		String fileName = extractFilenameFromFilepath(layerFilePath);
		
		if(fileName == null)
		{
			addNewLayerView.displayInfoMessage("The file path you typed is invalid!", "Can not open file", 2);
			System.out.println("can't open layer file: "+layerFilePath);
			return;
		}
		
		//create a serialized layer
		ImageSerializer layerSerializer = new  ImageSerializer(layerFilePath);
		String serializedLayer = layerSerializer.serializePhoto(); 
		
		//get the layer details
		int selectedLayerType = 0 ;
		for (int i = 0; i <  layerLists.size(); i++) 
		{
			LayerModel currLayer =  layerLists.get(0).get(i);
			String currLayerName = currLayer.getLayerName(); 
			 if(currLayerName.equals(selectedLayerName))
				 selectedLayerType = currLayer.getLayerType();
		}
		
		//get the photo timestamp and location
		SearchPhotoModel aPhoto = addNewLayersModel.getaPhoto();
		String timestamp = aPhoto.getTimeStamp();
		int location = aPhoto.getLocation();
		
		//send the layer + the insert/update query to DB
		String newLayerQuery ="addphoto";
		newLayerQuery	+= "c:/sgip/newsrc/"+fileName	+"INSERT INTO photolayer "
														+"(location,timestamp,layertype,linktofile) VALUES "
														+"(" + location + ",'" + timestamp + "',"+ selectedLayerType +"," + "'c:/sgip/newsrc/"+fileName+ "')";
		tempLayer = new LayerModel(selectedLayerType,selectedLayerName,"c:/sgip/newsrc/"+fileName);
		
		sendToServer(newLayerQuery+"<"+serializedLayer);
		
	}
	
	/**
	 * layer was added successfully to DB
	 */
	@Override
	public void handleDBRequestSuccess() 
	{
		if(!hasLayer)
		{
			//if the photo do not have this layer - add the new layer to layer list
			layerLists.get(0).add(tempLayer);
			setLayerListInGUI(); 
		}
		
		//inform the user - the layer was added!
		addNewLayerView.displayInfoMessage("New layer was added successfully to photo.", "Layer added", 1);
	}
	
	/**
	 * The listener interface for receiving addLayerButton events.
	 * The object created with that class is registered with a component using the
	 * component's <code>addAddLayerButtonListener<code> method. When
	 * the addLayerButton event occurs, that object's appropriate
	 * method is invoked. if it is the first time this button is clicked - all the layers that belongs this photo 
	 * are brought from the DB . else - addLayerToPhoto() is called. 
	 *
	 * @see AddLayerButtonEvent
	 */
	class AddLayerButtonListener extends AbstractListener
	{

		@Override
		public void actionPerformed(ActionEvent e) 
		{
			//if this is the first time this button is clicked - get all the photo layers
			if(layerLists.size() == 1)
			{
				//get all the photo layers
				SearchPhotoModel aPhoto = addNewLayersModel.getaPhoto();
				String timestamp = aPhoto.getTimeStamp();
				int location = aPhoto.getLocation();
				String searchPhotoLayersQuery =	"search" 	+"SELECT lt.LayerType , lt.LayerName , pl.LinkToFile "
															+"FROM photos AS p , photolayer AS pl, layertypes AS lt "
															+"WHERE pl.TimeStamp = p.TimeStamp "
															+"AND pl.Location = p.Location "
															+"AND lt.LayerType = pl.LayerType "
															+"AND p.TimeStamp = '"+timestamp+"' "
															+"AND p.Location = "+location;
				sendToServer(searchPhotoLayersQuery);
				
			}
			//else - add the layer to list and send the request to server
			else 
				addLayerToPhoto();
			
		}
	}
	
	/**
	 * Add the new layer to layer list and send the request to server
	 */
	private void addLayerToPhoto() 
	{
		//get the selected layer details
		ArrayList<LayerModel> allThePhotoLayers = layerLists.get(1);
		String selectedLayerName = (String) addNewLayerView.getSelectedItem();
		 
		hasLayer  = false;
		
		System.out.println("Selected Layer Type : " + selectedLayerName);
		
		//Search the layer in the photo layers list
		for (int i = 0; i < allThePhotoLayers.size(); i++) 
		{
			LayerModel curr = allThePhotoLayers.get(i);
			if(curr.getLayerName().equals(selectedLayerName))
			{
				hasLayer = true;
				break;
			}
		}
		
		//if the photo has the selected layer type - prompt the user to approve replace
		if(hasLayer == true )
		{
			// ask the user , are you sure?
			int result = JOptionPane.showConfirmDialog(addNewLayerView,
														"Are you sure you want to replace the layer '"+selectedLayerName+"' ?"
														,"Confirm Layer Replace"
														,JOptionPane.YES_NO_OPTION,
														JOptionPane.WARNING_MESSAGE);
			if(result == JOptionPane.NO_OPTION)
				return;
		}
		
		//if the replace is approved or the photo do not have this layer -
		// send the layer query and the layer file to the server
		
		//serialize the layer
		String layerFilePath = addNewLayerView.getLinkTofileTextField();
		String fileName = extractFilenameFromFilepath(layerFilePath);
		
		if(fileName == null)
		{
			addNewLayerView.displayInfoMessage("The file path ypu typed is invalid!", "Can not open file", 2);
			System.out.println("can't open layer file: "+layerFilePath);
			return;
		}
		
		//create a serialized layer
		ImageSerializer layerSerializer = new  ImageSerializer(layerFilePath);
		String serializedLayer = layerSerializer.serializePhoto(); 
		
		//get the layer details
		int selectedLayerType = 0 ;
		for (int i = 0; i <  layerLists.size(); i++) 
		{
			LayerModel currLayer =  layerLists.get(0).get(i);
			String currLayerName = currLayer.getLayerName(); 
			 if(currLayerName.equals(selectedLayerName))
				 selectedLayerType = currLayer.getLayerType();
		}
		
		//get the photo timestamp and location
		SearchPhotoModel aPhoto = addNewLayersModel.getaPhoto();
		String timestamp = aPhoto.getTimeStamp();
		int location = aPhoto.getLocation();
		String newLayerQuery ="addphoto";
		
		//send the layer + the insert/update query to DB
		if(!hasLayer)
		{
			newLayerQuery	+= "c:/sgip/newsrc/"+fileName	+"INSERT INTO photolayer "
															+"(location,timestamp,layertype,linktofile) VALUES "
															+"(" + location + ",'" + timestamp + "',"+ selectedLayerType +"," + "'c:/sgip/newsrc/"+fileName+ "')";
		}
		
		else
		{
			newLayerQuery	+= "c:/sgip/newsrc/"+fileName	+"UPDATE photolayer "
															+"SET LinkToFile = 'c:/sgip/newsrc/"+fileName+"' "
															+"WHERE Location = "+location+" "
															+"AND TimeStamp ='"+timestamp+"' "
															+"AND LayerType ='"+selectedLayerType+"'";
		}
		tempLayer = new LayerModel(selectedLayerType,selectedLayerName,"c:/sgip/newsrc/"+fileName);
		
		sendToServer(newLayerQuery+"<"+serializedLayer);
	}
	
	/**
	 * Extract filename from filepath.
	 *
	 * @param filepath the filepath
	 * @return the string
	 */
	private String extractFilenameFromFilepath(String filepath)
	{
		File f = new File(filepath);
		if((!f.canRead()) || (!f.exists()) || (f.isDirectory()) || (f.isHidden()) || (!f.canWrite()) )
			
			return null;
		System.out.println(f.getName());
		return f.getName();
	}
	
	/**
	 * Sets the layer list in GUI.
	 */
	private void setLayerListInGUI() 
	{
		ArrayList<LayerModel> allPossibleLayers = layerLists.get(0);
		layerNamesArray = new String[allPossibleLayers.size()];
		int i = 0;
		for (Iterator iterator = allPossibleLayers.iterator(); iterator.hasNext();i++) 
		{
			LayerModel layerModel = (LayerModel) iterator.next();
			layerNamesArray[i] =  layerModel.getLayerName();
		}
		addNewLayerView.setLayerList(layerNamesArray);
		addNewLayerView.addAddLayerActionListener(new AddLayerButtonListener());
	}
	
}
